package com.dq.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.URLDecoder;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.Map;
import java.util.Map.Entry;

/**
 * @创建人 gewenzhen
 * @创建时间 2019/9/10
 * @描述
 */
public class HttpUtil {
    private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);

    public static String mapToString(Map<String, String> args) {
        StringBuffer sb = new StringBuffer();
        for (Entry<String, String> entry : args.entrySet()) {
            sb.append(entry.getKey()).append(":").append(entry.getValue())
                    .append(",");
        }
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

    /**
     * 发送post请求
     *
     * @throws UnsupportedEncodingException
     */
    public static <T> T post(String URL, Map<String, String> params,
                             String postData, String contentType, String charset,
                             Class<T> jsonClass) {
        log.info("Url: " + URL + ", Request: " + mapToString(params));
        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams()
                .setConnectionTimeout(10000);
        client.getHttpConnectionManager().getParams().setSoTimeout(30000);
        PostMethod post = new PostMethod(URL);
        String contentTypeLocal = contentType;
        if (StringUtils.isBlank(contentTypeLocal)) {
            contentTypeLocal = "application/x-www-form-urlencoded";
        }
        post.addRequestHeader("Content-Type", contentType);
        post.addRequestHeader("Connection", "Close");// 是否保持连接
        post.addRequestHeader("Accept-Encoding", "identity");
        for (Map.Entry<String, String> entry : params.entrySet()) {
            post.addParameter(entry.getKey(), entry.getValue());
        }
        if (StringUtils.isNotBlank(postData)) {
            try {
                post.setRequestEntity(new StringRequestEntity(postData,
                        contentTypeLocal, charset));
            } catch (UnsupportedEncodingException e) {
                log.info("UnsupportedEncodingException:" + e.getMessage());
            }
        }
        try {
            client.executeMethod(post);
            String responseBody = new String(
                    post.getResponseBodyAsString().getBytes("iso-8859-1"),
                    "utf-8");
            log.info("Response: " + responseBody);
            if (post.getStatusCode() == 200) {
                T result = JSONObject.parseObject(responseBody, jsonClass);
                return result;
            } else {
                log.error("接口服务器返回值出错，错误码：" + post.getStatusCode() + "，返回信息："
                        + responseBody);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            post.releaseConnection();
            client.getHttpConnectionManager().closeIdleConnections(0);
        }
        return null;
    }

    /**
     * 发送post请求
     */
    public static <T> T get(String URL, Map<String, String> params,
                            String contentType, String charset, Class<T> jsonClass) {
        log.info("Url: " + URL + ", Request: " + mapToString(params));
        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams()
                .setConnectionTimeout(10000);
        client.getHttpConnectionManager().getParams().setSoTimeout(30000);
        GetMethod get = new GetMethod(URL);
        String contentTypeLocal = contentType;
        if (StringUtils.isBlank(contentTypeLocal)) {
            contentTypeLocal = "application/x-www-form-urlencoded";
        }
        get.addRequestHeader("Content-Type", contentType);
        get.addRequestHeader("Connection", "Close");// 是否保持连接
        get.addRequestHeader("Accept-Encoding", "identity");
        HttpMethodParams httpMethodParams = new HttpMethodParams();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            httpMethodParams.setParameter(entry.getKey(), entry.getValue());
        }
        get.setParams(httpMethodParams);
        try {
            client.executeMethod(get);
            String responseBody = new String(
                    get.getResponseBodyAsString().getBytes("iso-8859-1"),
                    "utf-8");
            log.info("Response: " + responseBody);
            if (get.getStatusCode() == 200) {
                T result = JSONObject.parseObject(responseBody, jsonClass);
                return result;
            } else {
                log.error("接口服务器返回值出错，错误码：" + get.getStatusCode() + "，返回信息："
                        + responseBody);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            get.releaseConnection();
            client.getHttpConnectionManager().closeIdleConnections(0);
        }
        return null;
    }

    /**
     * 判断url是否可达
     *
     * @throws IOException
     */
    public static boolean canUpToUrl(String urlStr) {
        log.debug("Url: " + urlStr);
        try {
            HttpClient client = new HttpClient();
            client.getHttpConnectionManager().getParams().setConnectionTimeout(10000);
            client.getHttpConnectionManager().getParams().setSoTimeout(30000);
            PostMethod post = new PostMethod(urlStr);
            post.addRequestHeader("Connection", "Close");// 是否保持连接
            post.addRequestHeader("Accept-Encoding", "identity");
            try {
                client.executeMethod(post);
                int statusCode = post.getStatusCode();
                return statusCode == 200;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            } finally {
                post.releaseConnection();
                client.getHttpConnectionManager().closeIdleConnections(0);
            }
        } catch (Throwable t) {
            return false;
        }
    }

    /**
     * 发送post请求
     */
    public static String post(String url, Map<String, String> args) {
        log.info("Url: " + url + ", Request: " + mapToString(args));
        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams()
                .setConnectionTimeout(10000);
        client.getHttpConnectionManager().getParams().setSoTimeout(30000);
        PostMethod post = new PostMethod(url);
        post.addRequestHeader("Content-type",
                "application/x-www-form-urlencoded");
        post.addRequestHeader("Connection", "Close");// 是否保持连接
        post.addRequestHeader("Accept-Encoding", "identity");
        for (Map.Entry<String, String> entry : args.entrySet()) {
            post.addParameter(entry.getKey(), entry.getValue());
        }
        try {
            client.executeMethod(post);
            String responseBody = post.getResponseBodyAsString();
            log.info("Response: " + responseBody);
            return responseBody;
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            post.releaseConnection();
            client.getHttpConnectionManager().closeIdleConnections(0);
        }
        return null;
    }

//    /**
//     * 发送post请求<br>
//     * 以key，value模式发送
//     *
//     * @throws IllegalAccessException
//     * @throws IllegalArgumentException
//     */
//    public static String post(String url, BaseRequest baseRequest) throws IllegalArgumentException, IllegalAccessException {
//        HttpClient client = new HttpClient();
//        client.getHttpConnectionManager().getParams()
//                .setConnectionTimeout(10000);
//        client.getHttpConnectionManager().getParams().setSoTimeout(30000);
//        PostMethod post = new PostMethod(url);
//        post.addRequestHeader("Content-type",
//                "application/x-www-form-urlencoded");
//        post.addRequestHeader("Connection", "Close");// 是否保持连接
//        post.addRequestHeader("Accept-Encoding", "identity");
//        Map<String, Object> map = baseRequest.parseMap();
//        for (String key : map.keySet()) {
//            post.addParameter(key, map.get(key) + "");
//        }
//        try {
//            client.executeMethod(post);
//            String responseBody = post.getResponseBodyAsString();
//            log.info("Response: " + responseBody);
//            return responseBody;
//        } catch (IOException e) {
//            log.error(e.getMessage(), e);
//        } finally {
//            post.releaseConnection();
//            client.getHttpConnectionManager().closeIdleConnections(0);
//        }
//        return null;
//    }

    /**
     * 以固定格式发送
     */
    public static String post(String url, String data, String charset) {
        log.info("Request: " + data);
        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams()
                .setConnectionTimeout(10000);
        client.getHttpConnectionManager().getParams().setSoTimeout(30000);
        PostMethod post = new PostMethod(url);
        post.addRequestHeader("Content-type",
                "application/x-www-form-urlencoded");
        post.addRequestHeader("Connection", "Close");// 是否保持连接
        post.addRequestHeader("Accept-Encoding", "identity");
        try {
            post.setRequestEntity(new StringRequestEntity(data,
                    "application/x-www-form-urlencoded", charset));
            client.executeMethod(post);
            String response = new String(
                    post.getResponseBodyAsString().getBytes("iso-8859-1"),
                    "utf-8");
            log.info("Response: " + response);
            return response;
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
        } finally {
            post.releaseConnection();
            client.getHttpConnectionManager().closeIdleConnections(0);
        }
        return null;
    }

    /**
     * 以固定格式发送<br>
     * 带证书
     */
    public static String posts(String url, String data, String path,
                               String certPwd)
            throws KeyStoreException, IOException, KeyManagementException,
            UnrecoverableKeyException, NoSuchAlgorithmException {
        log.info("Request: " + data);
        CloseableHttpResponse response = null;
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        FileInputStream instream = new FileInputStream(new File(path));// 加载本地的证书进行https加密传输
        try {
            keyStore.load(instream, certPwd.toCharArray());// 设置证书密码
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } finally {
            instream.close();
        }

        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, certPwd.toCharArray()).build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext, new String[]{"TLSv1"}, null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

        CloseableHttpClient client = HttpClients.custom()
                .setSSLSocketFactory(sslsf).build();

        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(new StringEntity(data, Consts.UTF_8));
        httpPost.setConfig(RequestConfig.custom().setConnectTimeout(30000)
                .setSocketTimeout(30000).build());
        try {
            response = client.execute(httpPost);
            HttpEntity entity = response.getEntity();
            log.info(new String(
                    EntityUtils.toString(entity).getBytes("iso-8859-1"),
                    "utf-8"));
            int statusCode = response.getStatusLine().getStatusCode();
            log.info(statusCode + "");
            if (HttpStatus.SC_OK == statusCode) {
                // 如果响应码是200
                String response1 = new String(
                        EntityUtils.toString(entity).getBytes("iso-8859-1"),
                        "utf-8");
                log.info("Response: " + response1);
                return response1;
            }
        } finally {
            if (response != null) {
                response.close();
            }
            if (httpPost != null) {
                httpPost.releaseConnection();
            }
            client.close();
        }
        return null;

    }



    private static PoolingHttpClientConnectionManager cm;
    private static void init() {
        if (cm == null) {
            cm = new PoolingHttpClientConnectionManager();
            // 整个连接池最大连接数
            cm.setMaxTotal(1000);
            // 每路由最大连接数，默认值是2
            cm.setDefaultMaxPerRoute(500);
        }
    }
    /**
     * 通过连接池获取HttpClient
     *
     * @return
     */
    private static CloseableHttpClient getHttpClient() {
        init();
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(15000)
                .setConnectionRequestTimeout(10000)
                .setSocketTimeout(30000).build();
        return HttpClients.custom()
                .setConnectionManager(cm)
                .setDefaultRequestConfig(requestConfig)
                .build();
    }

    public static JSONObject httpsGetRequest(String url, Map<String, String>  headers, String code, String msg) {
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse result = null;
        JSONObject jsonResult = null;
        HttpGet method = new HttpGet(url);
        ///设置连接超时5s
//        RequestConfig requestConfig = RequestConfig.custom()
//                .setConnectTimeout(5000).setConnectionRequestTimeout(5000)
//                .setSocketTimeout(10000).build();
//        method.setConfig(requestConfig);
        try {
            method.setHeader("Content-type", "application/json;charset=utf-8");
            if (headers != null && headers.size() > 0) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    method.setHeader(entry.getKey(), entry.getValue());
                }
            }
            result = httpClient.execute(method);
            InputStream in = result.getEntity().getContent();
            url = URLDecoder.decode(url, "UTF-8");

            // NO.001 请求发送成功，并得到响应
            if (result.getStatusLine().getStatusCode() == 200) {
                String str = "";
                //读取服务器返回过来的json字符串数据
                str = EntityUtils.toString(result.getEntity());
                //把json字符串转换成json对象
                jsonResult = (JSONObject) JSONObject.parse(str);
            } else {
                jsonResult = new JSONObject();
                jsonResult.put(code, result.getStatusLine().getStatusCode());
                jsonResult.put(msg, String.format("http-get请求失败; code: %s, url: %s", result.getStatusLine().getStatusCode(), url));
            }
            // NO.002 请求发送失败，得到响应数据可能为[null]
            if(jsonResult == null) {
                jsonResult = new JSONObject();
                jsonResult.put(code, -1);
                jsonResult.put(msg, String.format("http-get请求失败; code: %s, url: %s", -1, url));
            }

            in.close();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("http-get请求失败, url: " + url, e);
        } finally {
            if (result != null) {
                try {
                    result.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return jsonResult;
    }
}
